package source.events.action;

import source.model.*;
import source.model.type.ActionResult;
import source.model.type.GameObjectType;
import source.model.skill.Skill;
import source.view.MainScreen;

/**
 * Attack class is responsible for resolving combat with the current unit
 * and its move direction.  If the current selection isn't a unit the action
 * fails.
 * @author Joe
 *
 */
public class Attack implements Action{

	private GameObject go;
	private int pid;
	private Position posToAttack;


	public Attack(int pid, Position posToAttack){
		this.go = go;	
		this.pid = pid;
		this.posToAttack = posToAttack;
	}		

	public ActionResult execute( DataHandler dh ) {
		MainScreen.writeToConsole("Executing Attack");

		go = dh.getCurrentSelection();

		//First check to see if we had a unit do the attacking
		if(go.objectType() == GameObjectType.UNIT) {

			Unit attackingUnit = (Unit)go;	
			if(attackingUnit.getMissionFlag())
			{
				MainScreen.writeToConsole("Current selection is on a mission");
				return ActionResult.IMPOSSIBLE;
			}
			//Check to see if we're trying to attack something in range
			int xDistAway = Math.abs(dh.positionQuery(go.objectID()).getX() - posToAttack.getX());
			int yDistAway = Math.abs(dh.positionQuery(go.objectID()).getY() - posToAttack.getY());

			int distAway = Math.max(xDistAway, yDistAway) - 1;
			
			if(distAway < attackingUnit.getType().minRange() ||  distAway >  attackingUnit.getType().maxRange()){
				MainScreen.writeToConsole("Attack position is out of range");
				return ActionResult.IMPOSSIBLE;
			}

			//Now we actually pick a target to attack if there is one
			GameObject[] possibleUnitTargets = dh.positionQuery(posToAttack, GameObjectType.UNIT);
			GameObject[] possibleBaseTargets = dh.positionQuery(posToAttack, GameObjectType.BASE);

			//Some of those GameObjects belong to the current player though; have to make sure they're all for different players
			GameObject[] otherUnitTargets = new GameObject[possibleUnitTargets.length];
			int otherUnitTargetsInd = 0;
			GameObject[] otherBaseTargets = new GameObject[possibleBaseTargets.length];
			int otherBaseTargetsInd = 0;

			for(GameObject gocur : possibleUnitTargets){
				if(dh.playerIDQuery(gocur) != pid){
					otherUnitTargets[otherUnitTargetsInd] = gocur;
					otherUnitTargetsInd++;
				}
			}

			for(GameObject gocur : possibleBaseTargets){
				if(dh.playerIDQuery(gocur) != pid){
					otherBaseTargets[otherBaseTargetsInd] = gocur;
					otherBaseTargetsInd ++;
				}
			} 
				
			GameObject[] unitTargets = new GameObject[otherUnitTargetsInd];
			GameObject[] baseTargets = new GameObject[otherBaseTargetsInd];
	
			System.arraycopy(otherUnitTargets, 0, unitTargets, 0, otherUnitTargetsInd);
			System.arraycopy(otherBaseTargets, 0, baseTargets, 0, otherBaseTargetsInd);
	

			//If there are fleets over a base then we have to attack them first
			if(unitTargets.length != 0){
				double ranNum = Math.random();
				int target = (int) ( ranNum * (double)unitTargets.length );

				Unit gettingAttacked = (Unit)unitTargets[target];

				//Handle Sneaking
				double multiplier = -1;
		
				Skill sneakingskill = attackingUnit.getSkill("sneak");
				if(sneakingskill != null)	
					multiplier *= 1.25;
			
				
				MainScreen.writeToConsole("Before the attack, the attacking unit's hp is " + attackingUnit.currentHealth() + ".  The defending unit's hp is " + gettingAttacked.currentHealth());
				if(gettingAttacked.modHealth((int)(multiplier * attackingUnit.getType().oDamage()))){
					attackingUnit.modHealth(-1 * gettingAttacked.getType().dDamage());
					MainScreen.writeToConsole("After the attack, the attacking unit's hp is " + attackingUnit.currentHealth() + ".  The defending unit's hp is " + gettingAttacked.currentHealth());
					dh.returnObject(attackingUnit);
				}
				dh.returnObject(gettingAttacked);				
		
				return ActionResult.SUCCESS;
			} else if(baseTargets.length != 0){
				double ranNum = Math.random();
				int target = (int) ( ranNum * (double)baseTargets.length );
				
				Base gettingAttacked = (Base)baseTargets[target];	 						
				if(gettingAttacked.modHealth(-1 * attackingUnit.getType().oDamage())){
					attackingUnit.modHealth(-1 * gettingAttacked.getType().dDamage());
					dh.returnObject(attackingUnit);
				}
				dh.returnObject(gettingAttacked);
		
				return ActionResult.SUCCESS;
			} else{
				MainScreen.writeToConsole("No enemy units or bases on the targeted square");
				return ActionResult.IMPOSSIBLE;
			}
		} else{
			MainScreen.writeToConsole("Selected object is not a unit");
			return ActionResult.IMPOSSIBLE;
		}	
	}
}
